/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Dominio;

import ie.IEngine;
import ie.IEError;
import ie.IEPair;
import ie.IEValue;
import ie.IEType;
import ie.IEAbstractFunction;
import ie.TC;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;

/**
 *
 * @author aleixfdz
 */

/*
 * Definiciones propias de IERemigio:
 *
 * accion nos marca qué accion queremos calcular
 *  1 = elegir descarte/baraja
 *  2 = jugar: o descartamos uan carta, o cerramos con una partición
 *  3 = dar mejor partición...
 *
 * cartas son {int, int}
 *  Mapeo {palo, valor} //los comodines tienen valor 0 siguiendo cno lo definido en mazo
 *    palo : 0 -> "Comodin", 1 -> "Picas", 2 -> "Corazones", 3 -> "Diamantes", 4 -> "Treboles"
 *
 * jugador son int que identifican al jugador. va de 0 a n-1 donde n es el número total de jugadores
 *
 * puntos son int
 *
 * accion1 int    0 pillar de baraja, 1 pillar del descarte
 * acción2 int    0 cerrar, 1 descartar
 *
 *
 * Hechos globales que gestiona IERemigio
 *  accion    :: int
 *  log       :: [{int, int, C, C}] [{jugador, acción1, cartainicio, cartafin}]
 *  jugador   :: int                id de nuestro jugador, el siguiente jugador es +1, etc
 *  descarte  :: {int, int}         el descarte
 *  descartar :: {int, int}         la que descartamos
 *  mano      :: [C]
 *  particion :: [[C]]              mejor partición actual
 *  jugadores :: [int]              [puntos-jugador-ièsimo]
 *  reglas    :: {bool,             {5parejas
 *
 *  elegir    :: bool
 *  jugar     :: bool
 *
 * Primitivas
 *  mejor_espectativa
 *  mejor_mano
 *
 * Macros globales
 *  siguiente_jugador :: int -> [int] -> int
 *  siguiente_jugador a j = (a + 1) % #j
 */

public class IERemigio extends IEngine {

  public IERemigio(String s) throws Exception {
    super(s);
  }



  @Override
  protected void addFunction(List<IEPair<String, IEValue>> function) {
    IEValue value;
    // [{int, int}] -> [[[{int, int}]]]
    value = new IEValue(new IEType("idapp", new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))), new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))))));
    value.app = new IEAbstractFunction() {
      public IEValue app(int n, IEValue[] args) throws IEError {
        return grupos(args[0].l);
      }
    };
    function.add(new IEPair("grupos", value));

    // [{int, int}] -> [[[{int, int}]]]
    value = new IEValue(new IEType("idapp", new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))), new IEType(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)))))));
    value.app = new IEAbstractFunction() {
      public IEValue app(int n, IEValue[] args) throws IEError {
        return manos(args[0].l);
      }
    };
    function.add(new IEPair("manos", value));

    // [{int, int}] -> [[{int, int}]]
    value = new IEValue(new IEType("idapp", new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))), new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))))));
    value.app = new IEAbstractFunction() {
      public IEValue app(int n, IEValue[] args) throws IEError {
        return mejorparticion(args[0].l);
      }
    };
    function.add(new IEPair("mejorparticion", value));

    // [{int, int}] -> [[{int, int}]]
    value = new IEValue(new IEType("idapp", new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))), new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))))));
    value.app = new IEAbstractFunction() {
      public IEValue app(int n, IEValue[] args) throws IEError {
        return mejorgrupo(args[0].l);
      }
    };
    function.add(new IEPair("mejorgrupo", value));
  }

  //funciones para implementar primitivas
/* se podría hacer más eficiente, pero tampoco es necesario
  List<ci> submanosef(List<ci> mask) {
    List<ci> res;
    ci am = mask.get(0);
  


    if(mask.size() > 1) {
      res = submanosef(mask.subList(1, mask.size()));
      int m = res.size();
      for(int i = 0; i < m; ++i) {
        ci bm = res.get(i);
        if (comp(am, bm)) {
          res.add(mcomb(am, bm));
        }
      }
    }
    else {
      res = new ArrayList<ci>(1);
    }
    res.add(am);
    return res;
  }
 */

  IEValue submanos(List<IEValue> g, List<ci> mask) {
    IEValue v = new IEValue(new IEType(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))))));
    IEValue t = new IEValue(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)))));
    IEValue a = g.get(0);
    ci am = mask.get(0);
    v.l = new ArrayList<IEValue>(1);


    if(g.size() > 1) {
      List<ci> mm = new ArrayList<ci>(mask.subList(1, mask.size()));
      List<IEValue> tmp = submanos(g.subList(1, g.size()), mm).l;
      mask.clear();
      mask.addAll(mm);
      v.l = new ArrayList<IEValue>(tmp);
      Iterator<IEValue> it = tmp.iterator();
      Iterator<ci> m = mm.iterator();
      while (m.hasNext()) {
        ci bm = m.next();
        t = it.next();
        if (comp(am, bm)) {
          IEValue t0 = new IEValue(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)))));
          t0.l = new ArrayList(t.l);
          t0.l.add(a);
          v.l.add(t0);
          mask.add(mcomb(am, bm));
        }
      }
    }
    else {
      mask.clear();
    }
    IEValue t0 = new IEValue(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)))));
    t0.l = new ArrayList<IEValue>(1);
    t0.l.add(a);
    v.l.add(t0);
    mask.add(am);
    return v;
  }

  IEValue mejorparticion(List<IEValue> l) {
    Iterator<IEValue> it = manos(l).l.iterator();
    IEValue v = new IEValue(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)))));
    v.l = new ArrayList<IEValue>(0);
    int nc = 0;

    while(it.hasNext()) {
      IEValue t = it.next();
      int nt = numcartas(t);
      if (nt > nc && nt <= 10) {
        nc = nt;
        v = t;
      }
    }
    it = v.l.iterator();
    while (it.hasNext()) {
      if (it.next().l.size() < 3) {
        it.remove();
      }
    }
    return v;
  }

  IEValue mejorgrupo(List<IEValue> l) {
    Iterator<IEValue> it = manos(l).l.iterator();
    IEValue v = new IEValue(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)))));
    v.l = new ArrayList<IEValue>(0);
    int nc = 0;
    int s = 0;

    while(it.hasNext()) {
      IEValue t = it.next();
      int nt = numcartas(t);
      int ns = numcartastotal(t);
      if ((nt > nc && nt <= 10) || nt == nc && ns > s) {
        nc = nt;
        s = ns;
        v = t;
      }
    }
    it = v.l.iterator();
    while (it.hasNext()) {
      if (it.next().l.size() < 2) {
        it.remove();
      }
    }
    return v;
  }


  int numcartas(IEValue v) {
    Iterator<IEValue> it = v.l.iterator();
    int nc = 0;
    while (it.hasNext()) {
      IEValue t = it.next();
      if (t.l.size() > 2) {
        nc += t.l.size();
      }
    }
    return nc;
  }

  int numcartastotal(IEValue v) {
    Iterator<IEValue> it = v.l.iterator();
    int nc = 0;
    while (it.hasNext()) {
      IEValue t = it.next();
      if (t.l.size() > 1) {
        nc += t.l.size();
      }
    }
    return nc;
  }

  IEValue grupos(List<IEValue> l) {
    //eliminar comodines y dar el número máximo a subgrupos
    //siempre añadirà comodines si puede...
    ordenar(l);
    int nc = grupo_nc(l);
    List<ci> mask = new ArrayList<ci>(0);
    return subgrupos(l.subList(nc, l.size()), mask);
  }

    IEValue manos(List<IEValue> l) {
    //eliminar comodines y dar el número máximo a subgrupos
    //siempre añadirà comodines si puede...
    ordenar(l);
    int nc = grupo_nc(l);
    List<ci> mask = new ArrayList<ci>(0);
    IEValue v = subgrupos(l.subList(nc, l.size()), mask);
    Iterator<IEValue> it = v.l.iterator();
    Iterator<ci> itt = mask.iterator();
    while (it.hasNext()) {
      itt.next();
      if (it.next().l.size() < 2) {
        it.remove();
        itt.remove();
      }
    }

    return submanos(v.l, mask);
  }

  //pre ordenado...
  int grupo_nc(List<IEValue> g) {
    int nc = 0;
    Iterator<IEValue> it = g.iterator();
    while (it.hasNext() && comodin(it.next())) {
      ++nc;
    }
    return nc;
  }

  ci mset(int i) {
    return new ci(0x01 << i-1);
  }

  ci mcomb(ci i, ci j) {
    return new ci(i.i^j.i);
  }

  boolean comp(ci i, ci j) {
    return (i.i|j.i) == (i.i^j.i);
  }

  class ci {
    public int i;
    ci (int i) {
      this.i = i;
    }
    ci () {
    }
  };



  //pre ordenado sin comodines...
  IEValue subgrupos(List<IEValue> l, List<ci> mask) {
    IEValue a = l.get(0);
    ci ma = mset(l.size());

    IEValue v = new IEValue(new IEType(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)))));
    IEValue t = new IEValue(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))));
    
    

    if (l.size() > 1) {
      List<ci> mm = new ArrayList<ci>(0);
      IEValue s = subgrupos(l.subList(1, l.size()), mm);
      mask.clear();
      mask.addAll(mm);
      v.l = new ArrayList(s.l);
      Iterator<IEValue> it = s.l.iterator();
      Iterator<ci> m = mm.iterator();
      while (it.hasNext()) {
        t = it.next();
        ci tmpci = m.next();

        if ((mismovalor(a, t.l.get(0)) && mismovalor(a, t.l.get(t.l.size()-1)))
            || ((t.l.size() == 1 || (!mismovalor(a, t.l.get(0), 1))) && mismovalor(a, t.l.get(t.l.size()-1), 1))) {
          IEValue t0 = new IEValue(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))));
          t0.l = new ArrayList(t.l);
          t0.l.add(a);
          v.l.add(t0);

          mask.add(mcomb(ma, tmpci));
        }
      }
    }
    else {
      v.l = new ArrayList<IEValue>(1);
    }
    
    IEValue ml = new IEValue(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))));
    ml.l = new ArrayList<IEValue>(1);
    ml.l.add(a);
    v.l.add(ml);
    mask.add(ma);
    
    return v;
  }


  boolean mismovalor(IEValue v0, IEValue v1) {
    return v0.l.get(1).i ==  v1.l.get(1).i || comodin(v0) || comodin(v1);
  }

  boolean mismovalor(IEValue v0, IEValue v1, int i) {
    if (v0.l.get(1).i == 1 || v1.l.get(1).i == 1) {
    System.out.println(pvc(v0) + " " + i + " " + pvc(v1) + " "  + (v0.l.get(1).i == 1 && v1.l.get(1).i == 13) + " " + v0.l.get(1).i + " " + v1.l.get(1).i);
    }
    return v0.l.get(1).i+i ==  v1.l.get(1).i
      || comodin(v0) || comodin(v1) ||
      (v0.l.get(1).i == 1 && v1.l.get(1).i == 13);
  }

  boolean comodin(IEValue v) {
    return v.l.get(0).i == 0;
  }




  void ordenar(List<IEValue> l) {
    Collections.sort(l, new CC());
  }

  class CC implements Comparator {
    public int compare(Object a, Object b) {
      return ((IEValue) a).l.get(1).i - ((IEValue) b).l.get(1).i;
    }
  };


  IEValue mejor_expectativa(IEValue mano) {
    IEValue v = grupos(mano.l);
    //seleccionar unos grupos
    return v;
  }



  //funciones para comunicarse con IERemigio
  public void repartir(int i, Mazo mano, int[] p, Reglas r) {
    IEPair[] k = new IEPair[5];
    k[0] = new IEPair("jugador", i2v(i));
    k[1] = new IEPair("jugadores", jugadores(p));
    k[2] = new IEPair("reglas", reglas(r));
    k[3] = new IEPair("mano", m2v(mano));
    IEValue v = new IEValue(new IEType(new IEType("tuple", new IEType(TC.ti, new IEType(TC.ti, new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)), new IEType("tuple", new IEType(TC.ti, TC.ti))))))));
    v.l = new ArrayList<IEValue>(0);
    k[4] = new IEPair("log", v);
    setKnowledge(k);
  }
  
  public Jugada elegir(Mazo mano, Carta descarte) {
    IEPair[] k = new IEPair[3];
    k[0] = new IEPair("descarte", c2v(descarte));
    k[1] = new IEPair("mano", m2v(mano));
    k[2] = new IEPair("accion", i2v(1));
    setKnowledge(k);
    Jugada j = new Jugada(Jugada.BARAJA);
    try {
      if (resolution("elegir").b) {
        System.out.println("|| DESCARTE");
        printRemigio();
        j.setAccion(Jugada.DESCARTE);
      }
      else {
        System.out.println("|| BARAJA");
        printRemigio();
        j.setAccion(Jugada.BARAJA);
      }
    }
    catch (Exception e) {
      //qué hacer en este caso
    }
    return j;
  }

  public Jugada juega(Mazo mano) {
    IEPair[] k = new IEPair[2];
    k[0] = new IEPair("mano", m2v(mano));
    k[1] = new IEPair("accion", i2v(2));
    setKnowledge(k);
    Jugada j = new Jugada(Jugada.BARAJA);
    try {
      if (resolution("jugar").b) {
        System.out.println("|| DESCARTAR");
        printRemigio();
        j.setAccion(Jugada.DESCARTAR);
        j.setPosicion(descartar(mano));
        //devolver la carta...
      }
      else {
        System.out.println("|| PARTICION");
        printRemigio();
        j = particion(mano);
      }
    }
    catch (Exception e) {
      //qué hacer en este caso
    }
    return j;
  }

  public void accion(int jugador, Carta c0, int accion, Carta descarte) {
    //aquí sólo se modifica log
    IEValue v = getKnowledge("log");
    v.l.add(log2v(jugador, c0, accion, descarte));
    //se ha de hacer el set... no es necesario, creo, diría, y tal
  }

  public Jugada particion(Mazo mano) {
    IEValue p = getKnowledge("particion");
    Iterator<IEValue> it = p.l.iterator();
    while (it.hasNext()) {
      if (it.next().l.size() < 3) {
        it.remove();
      }
    }
    //elimina los grupos menores que 3
    Jugada j = new Jugada(Jugada.CERRAR, v2p(p, mano));
    return j;
  }


  //func auxiliars de conversión de tipos
  //
  //
  //para que se note la separación...
  boolean id(Carta c1, Carta c2) {
    System.out.println(pc(c1) + " <> " + pc(c2));
    return c1.getPalo().equals(c2.getPalo()) && c1.getValor() == c2.getValor();
  }

  int descartar(Mazo mano) {
    IEValue p = getKnowledge("descartar");
    return posicion(mano, v2c(p));
  }

  int posicion(Mazo mano, Carta c) {
    for (int i = 1; i < mano.vmazo.size()+1; ++i) {
      Carta cm = mano.consultar_carta(i);
      if (id(cm, c)) {
        return i;
      }
    }
    return 1;
  }

  ArrayList<int[]> v2p(IEValue v, Mazo mano) {
    Mazo mtmp = new Mazo(0, true);
    for (int i = mano.vmazo.size(); i >= 1; --i) {
      Carta c = new Carta();
      c.setPalo(mano.consultar_cpalo(i));
      c.setValor(mano.consultar_cvalor(i), 0);
      mtmp.introducir_carta(1, c);
    }
    ArrayList<int[]> p = new ArrayList<int[]>(0);
    Iterator<IEValue> i = v.l.iterator();
    while (i.hasNext()) {
      p.add(v2ai(i.next(), mtmp));
    }
    return p;
  }

  int[] v2ai(IEValue v, Mazo mano) {
    int[] a = new int[v.l.size()];
    Iterator<IEValue> it = v.l.iterator();
    for (int i = a.length - 1; i >= 0; --i) {
      IEValue t = it.next();
      a[i] = posicion(mano, v2c(t));
      Carta c = mano.consultar_carta(a[i]);
      c.setValor(-1, 0);
    }
    return a;
  }

  Carta v2c(IEValue v) {
    Carta c = new Carta();
    c.setPalo(i2p(v.l.get(0).i));
    c.setValor(v.l.get(1).i, 0);
    return c;
  }

  IEValue log2v(int i, Carta c0, int a, Carta c1) {
    IEValue v = new IEValue(new IEType("tuple", new IEType(TC.ti, new IEType(TC.ti, new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti)), new IEType("tuple", new IEType(TC.ti, TC.ti)))))));
    v.l = new ArrayList<IEValue>(4);
    v.l.add(i2v(i));
    v.l.add(i2v(a));
    v.l.add(c2v(c0));
    v.l.add(c2v(c1));
    return v;
  }

  IEValue i2v(int i) {
    IEValue v = new IEValue(TC.ti);
    v.i = i;
    return v;
  }

  IEValue jugadores(int[] j) {
    IEValue v = new IEValue(TC.tli);
    v.l = new ArrayList<IEValue>(j.length);
    for (int i = 0; i < j.length; ++i) {
      IEValue t = new IEValue(TC.ti);
      t.i = j[i];
      v.l.add(t);
    }
    return v;
  }

  IEValue reglas(Reglas r) {
    return new IEValue(TC.te);
  }

  IEValue c2v(Carta c) {
    IEValue v = new IEValue(new IEType("tuple", new IEType(TC.ti, TC.ti)));
    v.l = new ArrayList<IEValue>();
    IEValue t0 = new IEValue(TC.ti), t1 = new IEValue(TC.ti);
    t0.i = p2i(c.getPalo());
    t1.i = c.getValor();
    v.l.add(t0);
    v.l.add(t1);
    return v;
  }

  int p2i(String s) {
    //palo : 0 -> "Comodin", 1 -> "Picas", 2 -> "Corazones", 3 -> "Diamantes", 4 -> "Treboles"
    if (s.equals("Comodin")) {
      return 0;
    }
    else if (s.equals("Picas")) {
      return 1;
    }
    else if (s.equals("Corazones")) {
      return 2;
    }
    else if (s.equals("Diamantes")) {
      return 3;
    }
    else {
      return 4;
    }
  }

  String i2p(int i) {
    switch (i) {
      case 0: return "Comodin";
      case 1: return "Picas";
      case 2: return "Corazones";
      case 3: return "Diamantes";
      default: return "Treboles";
    }
  }

  IEValue m2v(Mazo m) {
    IEValue v = new IEValue(new IEType(new IEType("tuple", new IEType(TC.ti, TC.ti))));
    v.l = new ArrayList<IEValue>(0);
    for(int i = 0; i < m.vmazo.size(); ++i) {
      v.l.add(c2v(m.consultar_carta(i+1)));
    }
    return v;
  }

  Mazo v2m(IEValue v) {
    Mazo m = new Mazo(0, true);
    Iterator<IEValue> it = v.l.iterator();
    while (it.hasNext()) {
      m.introducir_carta(1, v2c(it.next()));
    }
    return m;
  }










  //funciones para imprimir mensajes...



  private String pvm(List<IEValue> l) {
    Iterator<IEValue> it = l.iterator();
    String s = "";
    while (it.hasNext()) {
      s += " " + pvc(it.next());
    }
    return s;
  }

  private String pvc(IEValue l) {
    return "「" + i2p(l.l.get(0).i).substring(0, 4) + "：" + v2s(l.l.get(1).i) + "」";
  }

  private String v2s(int i) {
    if (i == 0) {
      return "ø";
    }
    if (i == 11) {
      return "J";
    }
    if (i == 12) {
      return "Q";
    }
    if (i == 13) {
      return "K";
    }
    String s = "";
    s += i;
    return s;
  }







    private String pm(Mazo m) {
    String s = "";
    for (int i = 1; i <= m.vmazo.size(); ++i) {
      s += " " + pc(m.consultar_carta(i));
    }
    return s;
  }

  private String pc(Carta c) {
    return "「" + c.getPalo().substring(0, 4) + "：" + v2s(c.getValor()) + "」";
  }


  public void printRemigio() {
    
    IEValue descarte = getKnowledge("descarte");
    IEValue descartar = getKnowledge("descartar");
    IEValue mano = getKnowledge("mano");
    IEValue particion = getKnowledge("particion");
    IEValue mparticion = getKnowledge("mpart");
    if (descarte != null) {
    System.out.println("|| descarte         " +pvc(descarte));
    }
    if (mano != null) {
    System.out.println("|| mano            " +pm(v2m(mano)));
    }
    if (descartar != null) {
    System.out.println("|| descartar        " +pvc(descartar));
    }
    if (particion != null) {
    System.out.println("|| particion       " +pp(particion));
    }
    if (mparticion != null) {
    System.out.println("|| mparticion      " +pp(mparticion));
    }
  }

  private String pp(IEValue v) {
    String s = "";
    Iterator<IEValue> it = v.l.iterator();
    while (it.hasNext()) {
      s += pm(v2m(it.next()));
      if (it.hasNext()) {
        s += " ++ ";
      }
    }
    return s;
  }
}